<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="sM1Ep" id="sM1Ep"><span data-lake-id="u36719986" id="u36719986">典型回答</span></h1>
  <p data-lake-id="ucc88bc63" id="ucc88bc63"><br></p>
  <p data-lake-id="u4611cd91" id="u4611cd91"><span data-lake-id="u21f4c4c5" id="u21f4c4c5">字符串常量池中的常量有两种来源：</span></p>
  <p data-lake-id="uc8a3c6b8" id="uc8a3c6b8"><span data-lake-id="u467ac0ba" id="u467ac0ba">​</span><br></p>
  <p data-lake-id="u94d41a2b" id="u94d41a2b"><span data-lake-id="u9024795f" id="u9024795f">1、字面量会在编译期先进入到Class常量池，然后再在运行期进去到字符串池，</span></p>
  <p data-lake-id="u93aa097d" id="u93aa097d"><span data-lake-id="udc5dbbad" id="udc5dbbad">2、在运行期通过intern将字符串对象手动添加到字符串常量池中。</span></p>
  <p data-lake-id="u323acf5a" id="u323acf5a"><span data-lake-id="u0418431a" id="u0418431a">​</span><br></p>
  <blockquote data-lake-id="u5cbb6d4d" id="u5cbb6d4d">
   <p data-lake-id="u501cf56f" id="u501cf56f"><span data-lake-id="u5a3dd8b1" id="u5a3dd8b1">字面量：</span><a href="https://www.yuque.com/hollis666/vhr2ge/pwmmw153wb4f2cgq#AfDwW" data-lake-id="uf0194688" id="uf0194688"><span data-lake-id="u254f7690" id="u254f7690">https://www.yuque.com/hollis666/vhr2ge/pwmmw153wb4f2cgq#AfDwW</span></a></p>
  </blockquote>
  <p data-lake-id="u3cec0177" id="u3cec0177"><br></p>
  <p data-lake-id="u690d3ae5" id="u690d3ae5"><span data-lake-id="u37f5c3a2" id="u37f5c3a2">intern的作用是这样的：</span></p>
  <p data-lake-id="u0e9ba667" id="u0e9ba667"><span data-lake-id="uf725c577" id="uf725c577">​</span><br></p>
  <p data-lake-id="u7ba264e1" id="u7ba264e1"><strong><span data-lake-id="u03f910c3" id="u03f910c3">如果字符串池中已经存在一个等于该字符串的对象，intern()方法会返回这个已存在的对象的引用。</span></strong></p>
  <p data-lake-id="u20e14b34" id="u20e14b34"><strong><span data-lake-id="u1a723272" id="u1a723272">​</span></strong><br></p>
  <p data-lake-id="u7705db70" id="u7705db70"><strong><span data-lake-id="uad1493f2" id="uad1493f2">如果字符串池中没有等于该字符串的对象，intern()方法会将该字符串添加到字符串池中，并返回对新添加的字符串对象的引用。</span></strong></p>
  <p data-lake-id="u85867698" id="u85867698"><strong><span data-lake-id="u7e1c015f" id="u7e1c015f">​</span></strong><br></p>
  <pre lang="java"><code>
String s = new String("Hollis") + new String("Chuang");
s.intern();
</code></pre>
  <p data-lake-id="u6e6f6a02" id="u6e6f6a02"><strong><span data-lake-id="u558d5f2d" id="u558d5f2d">​</span></strong><br></p>
  <p data-lake-id="u4ff73585" id="u4ff73585"><strong><span data-lake-id="u4f68e210" id="u4f68e210">​</span></strong><br></p>
  <p data-lake-id="udc457ff3" id="udc457ff3"><span data-lake-id="u74c61c5a" id="u74c61c5a">所以，无论何时通过intern()方法获取字符串的引用，都会得到字符串池中的引用，这样可以确保相同的字符串在内存中只有一个实例。</span></p>
  <p data-lake-id="u6f84ab4d" id="u6f84ab4d"><span data-lake-id="uc79452fd" id="uc79452fd">​</span><br></p>
  <p data-lake-id="u52748011" id="u52748011"><span data-lake-id="uff5b088e" id="uff5b088e">很多人以为知道以上信息，就算是了解intern了，那么请回答一下这个问题：</span></p>
  <p data-lake-id="u5da8920b" id="u5da8920b"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    String s1 = new String("a"); 
    s1.intern(); 
    String s2 = "a";
    System.out.println(s1 == s2); // false
    
    String s3 = new String("a") + new String("a");
    s3.intern();
    String s4 = "aa";
    System.out.println(s3 == s4);//  true
}
</code></pre>
  <p data-lake-id="u1868e69f" id="u1868e69f"><br></p>
  <p data-lake-id="uc7b97365" id="uc7b97365"><span data-lake-id="u48f0a3d1" id="u48f0a3d1">大家可以在 JDK 1.7以上版本中尝试运行以上两段代码，就会发现，s1 == s2的结果是 false，但是s3 == s4的结果是 true。</span></p>
  <p data-lake-id="u7e7cde45" id="u7e7cde45"><span data-lake-id="ua72a5fee" id="ua72a5fee">​</span><br></p>
  <p data-lake-id="ufe635acf" id="ufe635acf"><strong><span data-lake-id="u65ad04dd" id="u65ad04dd">这是为什么呢？（后文所有case均基于JDK 1.8运行）</span></strong></p>
  <h1 data-lake-id="Yd1XF" id="Yd1XF"><span data-lake-id="ud5dd8ed3" id="ud5dd8ed3">扩展知识</span></h1>
  <p data-lake-id="uf419f283" id="uf419f283"><br></p>
  <h2 data-lake-id="gpwWO" id="gpwWO"><span data-lake-id="u94373a4b" id="u94373a4b">字符串常量进入常量池的时机</span></h2>
  <p data-lake-id="u79337621" id="u79337621"><br></p>
  <p data-lake-id="ua273575c" id="ua273575c"><span data-lake-id="ue1a8b944" id="ue1a8b944">​</span><br></p>
  <h2 data-lake-id="l7j8R" id="l7j8R"><span data-lake-id="uc3765138" id="uc3765138">intern原理</span></h2>
  <p data-lake-id="u681cfd4d" id="u681cfd4d"><br></p>
  <p data-lake-id="u06eae0eb" id="u06eae0eb"><span data-lake-id="ud4ee5e8e" id="ud4ee5e8e">先看一下上面这篇，了解了字符串常量进入常量池的时机之后，我们再回过头分析一下前面的例子：<br><br></span></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    String s1 = new String("a"); // ①
    s1.intern(); // ②
    String s2 = "a";// ③
    System.out.println(s1 == s2); // ④   false
    
    String s3 = new String("a") + new String("a");// ⑤
    s3.intern();// ⑥
    String s4 = "aa";// ⑦
    System.out.println(s3 == s4);// ⑧    true

}
</code></pre>
  <p data-lake-id="ufbb3c190" id="ufbb3c190"><br></p>
  <p data-lake-id="uc57b4cc0" id="uc57b4cc0"><br></p>
  <p data-lake-id="uea8b6e94" id="uea8b6e94"><span data-lake-id="ua5089167" id="ua5089167">这个类被编译后，Class常量池中应该有"a"和"aa"这两个字符串，这两个字符串最终会进到字符串池。</span><strong><span data-lake-id="u4b811f81" id="u4b811f81">但是，字面量"a"在代码①这一行，就会被存入字符串池，而字面量"aa"则是在代码⑦这一行才会存入字符串池。</span></strong></p>
  <p data-lake-id="u418710fe" id="u418710fe"><span data-lake-id="uc7688a7b" id="uc7688a7b">​</span><br></p>
  <p data-lake-id="u4b02e8e1" id="u4b02e8e1"><span data-lake-id="u6d6e6782" id="u6d6e6782">以上代码的执行过程：</span></p>
  <p data-lake-id="u61820e1e" id="u61820e1e"><span data-lake-id="u6e41b624" id="u6e41b624">第①行，new 一个 String 对象，并让 s1指向他。</span></p>
  <p data-lake-id="u12755419" id="u12755419"><span data-lake-id="u90ae9d59" id="u90ae9d59">第②行，对 s1执行 intern，但是因为"a"这个字符串已经在字符串池中，所以会直接返回原来的引用，但是并没有赋值给任何一个变量。</span></p>
  <p data-lake-id="u5b4227ff" id="u5b4227ff"><span data-lake-id="u1822234a" id="u1822234a">第③行，s2指向常量池中的"a"；</span></p>
  <p data-lake-id="u14d5f3a2" id="u14d5f3a2"><span data-lake-id="ub0a03957" id="ub0a03957">​</span><br></p>
  <p data-lake-id="ua43cb43e" id="ua43cb43e"><span data-lake-id="uf07c7648" id="uf07c7648">所以，s1和 s2并不相等！</span></p>
  <p data-lake-id="u7881a3e9" id="u7881a3e9"><span data-lake-id="ua0d24568" id="ua0d24568">​</span><br></p>
  <p data-lake-id="ue7a0e90a" id="ue7a0e90a"><span data-lake-id="u20f02ee7" id="u20f02ee7">第⑤行，new 一个 String 对象，并让 s3 指向他。</span></p>
  <p data-lake-id="u7c00a3cd" id="u7c00a3cd"><span data-lake-id="u635f9cc2" id="u635f9cc2">第⑥行，对 s3 执行 intern，但是目前字符串池中还没有"aa"这个字符串，于是会把&lt;s3指向的String对象的引用&gt;放入&lt;字符串常量池&gt;</span></p>
  <p data-lake-id="u5716ca83" id="u5716ca83"><span data-lake-id="u79f359e8" id="u79f359e8">第⑦行，因为"aa"这个字符串已经在字符串池中，所以会直接返回原来的引用，并赋值给 s4；</span></p>
  <p data-lake-id="u02b7168b" id="u02b7168b"><span data-lake-id="uc656f1fe" id="uc656f1fe">​</span><br></p>
  <p data-lake-id="u68b7b27b" id="u68b7b27b"><span data-lake-id="u1bc6932d" id="u1bc6932d">所以，s3和 s4 相等！</span></p>
  <p data-lake-id="u3de131e4" id="u3de131e4"><span data-lake-id="u59ffa764" id="u59ffa764">​</span><br></p>
  <p data-lake-id="uef3c564f" id="uef3c564f"><span data-lake-id="u8dca3b91" id="u8dca3b91">而如果我们对代码稍作修改：</span></p>
  <p data-lake-id="u7dd21ea9" id="u7dd21ea9"><span data-lake-id="ub0ffc009" id="ub0ffc009">​</span><br></p>
  <pre lang="java"><code>
String s = "aa";// ①
String s3 = new String("a") + new String("a");// ②
s3.intern();// ③
String s4 = "aa";
System.out.println(s3 == s4);// ④
</code></pre>
  <p data-lake-id="udfffd689" id="udfffd689"><span data-lake-id="u82dccc41" id="u82dccc41">​</span><br></p>
  <p data-lake-id="ud818487c" id="ud818487c"><span data-lake-id="uecc9006b" id="uecc9006b">以上代码得到的结果则是：false</span></p>
  <p data-lake-id="u5203474b" id="u5203474b"><span data-lake-id="u501e2c6b" id="u501e2c6b">​</span><br></p>
  <p data-lake-id="u0f5b8903" id="u0f5b8903"><span data-lake-id="u67c1d10f" id="u67c1d10f">第①行，创建一个字符串aa，并且因为它是字面量，所以把他放到字符串池。</span></p>
  <p data-lake-id="u70dc0acb" id="u70dc0acb"><span data-lake-id="ue7ee12f1" id="ue7ee12f1">第②行，new一个 String 对象，并让 s3 指向他。</span></p>
  <p data-lake-id="u3d9c0aa0" id="u3d9c0aa0"><span data-lake-id="u5eafce1d" id="u5eafce1d">第③行，对 s3 执行 intern，但是目前字符串池中已经有"aa"这个字符串，所以会直接返回s的引用，但是并没有对s3进行赋值</span></p>
  <p data-lake-id="u020dd4cf" id="u020dd4cf"><span data-lake-id="ua48cd588" id="ua48cd588">第④行，因为"aa"这个字符串已经在字符串池中，所以会直接返回原来的引用，即s的引用，并赋值给 s4；所以，s3和 s4 不相等。</span></p>
  <p data-lake-id="uc89acf58" id="uc89acf58"><span data-lake-id="uccbe58c5" id="uccbe58c5">​</span><br></p>
  <h2 data-lake-id="sGvlN" id="sGvlN"><span data-lake-id="u89387b8a" id="u89387b8a">a和1有什么不同</span></h2>
  <p data-lake-id="u7d4694c5" id="u7d4694c5"><br></p>
  <p data-lake-id="u3b28228f" id="u3b28228f"><span data-lake-id="u05477a03" id="u05477a03">关于这个问题，我们还有一个变型，可以帮大家更好的理解intern，请大家分别在JDK 1.8和JDK 11及以上的版本中执行以下代码：</span></p>
  <p data-lake-id="u5db5c328" id="u5db5c328"><br></p>
  <pre lang="java"><code>
String s3 = new String("1") + new String("1");// ①
s3.intern();// ②
String s4 = "11";
System.out.println(s3 == s4);// ③
</code></pre>
  <p data-lake-id="ufd4ad078" id="ufd4ad078"><br></p>
  <p data-lake-id="u64c042ed" id="u64c042ed"><span data-lake-id="uf8c7cd86" id="uf8c7cd86">你会发现，在JDK 1.8中，以上代码得到的结果是true，而JDK 11及以上的版本中结果却是false。</span></p>
  <p data-lake-id="ubb70dbb2" id="ubb70dbb2"><span data-lake-id="uc1ab9eca" id="uc1ab9eca">​</span><br></p>
  <p data-lake-id="u2733c683" id="u2733c683"><span data-lake-id="ua9ae7779" id="ua9ae7779">那么，再稍作修改呢？在目前的所有JDK版本中，执行以下代码：</span></p>
  <p data-lake-id="u0f68eedc" id="u0f68eedc"><span data-lake-id="uc52bb45b" id="uc52bb45b">​</span><br></p>
  <pre lang="java"><code>
String s3 = new String("3") + new String("3");// ①
s3.intern();// ②
String s4 = "33";
System.out.println(s3 == s4);// ③
</code></pre>
  <p data-lake-id="ue65df85a" id="ue65df85a"><br></p>
  <p data-lake-id="u6965426c" id="u6965426c"><span data-lake-id="u99180e50" id="u99180e50">得到的结果也是true，你知道为什么嘛？</span></p>
  <p data-lake-id="ua4e87115" id="ua4e87115"><span data-lake-id="u209a1fd5" id="u209a1fd5">​</span><br></p>
  <p data-lake-id="ua6230801" id="ua6230801"><span data-lake-id="ub5881a6d" id="ub5881a6d">答案在下文中：</span></p>
  <p data-lake-id="u3a8dc555" id="u3a8dc555"><span data-lake-id="uaa8b771a" id="uaa8b771a">​</span><br></p>
 </body>
</html>